import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class homework_08 extends Application {

    // 定义裁剪窗口的边界
    private static final int X_MIN = 100;
    private static final int X_MAX = 300;
    private static final int Y_MIN = 100;
    private static final int Y_MAX = 300;

    // 定义区域码的位数
    private static final int INSIDE = 0; // 0000
    private static final int LEFT = 1;   // 0001
    private static final int RIGHT = 2;  // 0010
    private static final int BOTTOM = 4; // 0100
    private static final int TOP = 8;    // 1000

    @Override
    public void start(Stage primaryStage) {
        Canvas canvas = new Canvas(400, 400);
        GraphicsContext gc = canvas.getGraphicsContext2D();

        // 绘制红色虚线边框
        drawDashedRectangle(gc, X_MIN, Y_MIN, X_MAX - X_MIN, Y_MAX - Y_MIN, Color.RED);

        // 定义线段的起点和终点
        int x1 = 50, y1 = 150; // 起点
        int x2 = 350, y2 = 250; // 终点

        // 绘制原始线段（蓝色）
        drawLine(gc, x1, y1, x2, y2, Color.BLUE);

        // 使用 Cohen-Sutherland 算法裁剪线段
        int[] clipped = cohenSutherlandClip(x1, y1, x2, y2);
        if (clipped != null) {
            // 绘制裁剪后的线段（黄色）
            drawLine(gc, clipped[0], clipped[1], clipped[2], clipped[3], Color.YELLOW);
        }

        StackPane root = new StackPane(canvas);
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Cohen-Sutherland Clipping with DDA");
        primaryStage.show();
    }

    /**
     * Cohen-Sutherland 裁剪算法
     *
     * @param x1 线段起点的x坐标
     * @param y1 线段起点的y坐标
     * @param x2 线段终点的x坐标
     * @param y2 线段终点的y坐标
     * @return 如果线段在裁剪窗口内或与裁剪窗口相交，返回裁剪后的线段端点；否则返回null
     */
    private int[] cohenSutherlandClip(int x1, int y1, int x2, int y2) {
        int code1 = computeRegionCode(x1, y1); // 计算起点的区域码
        int code2 = computeRegionCode(x2, y2); // 计算终点的区域码

        while (true) {
            if ((code1 | code2) == 0) {
                // 线段完全在裁剪窗口内
                return new int[]{x1, y1, x2, y2};
            } else if ((code1 & code2) != 0) {
                // 线段完全在裁剪窗口外
                return null;
            } else {
                // 线段部分在裁剪窗口内，需要裁剪
                int x = 0, y = 0;
                int codeOut = (code1 != 0) ? code1 : code2; // 选择一个在窗口外的点

                // 计算交点
                if ((codeOut & TOP) != 0) {
                    // 与上边界相交
                    x = x1 + (x2 - x1) * (Y_MAX - y1) / (y2 - y1);
                    y = Y_MAX;
                } else if ((codeOut & BOTTOM) != 0) {
                    // 与下边界相交
                    x = x1 + (x2 - x1) * (Y_MIN - y1) / (y2 - y1);
                    y = Y_MIN;
                } else if ((codeOut & RIGHT) != 0) {
                    // 与右边界相交
                    y = y1 + (y2 - y1) * (X_MAX - x1) / (x2 - x1);
                    x = X_MAX;
                } else if ((codeOut & LEFT) != 0) {
                    // 与左边界相交
                    y = y1 + (y2 - y1) * (X_MIN - x1) / (x2 - x1);
                    x = X_MIN;
                }

                // 更新线段端点
                if (codeOut == code1) {
                    x1 = x;
                    y1 = y;
                    code1 = computeRegionCode(x1, y1);
                } else {
                    x2 = x;
                    y2 = y;
                    code2 = computeRegionCode(x2, y2);
                }
            }
        }
    }

    /**
     * 计算点的区域码
     *
     * @param x 点的x坐标
     * @param y 点的y坐标
     * @return 区域码
     */
    private int computeRegionCode(int x, int y) {
        int code = INSIDE;

        if (x < X_MIN) {
            code |= LEFT;
        } else if (x > X_MAX) {
            code |= RIGHT;
        }

        if (y < Y_MIN) {
            code |= BOTTOM;
        } else if (y > Y_MAX) {
            code |= TOP;
        }

        return code;
    }

    /**
     * 使用 DDA 算法画线
     *
     * @param gc  GraphicsContext
     * @param x1 起点x坐标
     * @param y1 起点y坐标
     * @param x2 终点x坐标
     * @param y2 终点y坐标
     * @param color 线条颜色
     */
    private void drawLine(GraphicsContext gc, int x1, int y1, int x2, int y2, Color color) {
        gc.setStroke(color);

        int dx = x2 - x1;
        int dy = y2 - y1;
        int steps = Math.max(Math.abs(dx), Math.abs(dy)); // 计算步数

        float xIncrement = (float) dx / steps;
        float yIncrement = (float) dy / steps;

        float x = x1;
        float y = y1;

        for (int i = 0; i <= steps; i++) {
            gc.strokeOval(Math.round(x), Math.round(y), 1, 1); // 绘制像素点
            x += xIncrement;
            y += yIncrement;
        }
    }

    /**
     * 绘制虚线矩形
     *
     * @param gc  GraphicsContext
     * @param x 矩形左上角x坐标
     * @param y 矩形左上角y坐标
     * @param width 矩形宽度
     * @param height 矩形高度
     * @param color 线条颜色
     */
    private void drawDashedRectangle(GraphicsContext gc, int x, int y, int width, int height, Color color) {
        gc.setStroke(color);
        gc.setLineDashes(5); // 设置虚线样式

        // 绘制矩形的四条边
        drawLine(gc, x, y, x + width, y, color); // 上边
        drawLine(gc, x + width, y, x + width, y + height, color); // 右边
        drawLine(gc, x + width, y + height, x, y + height, color); // 下边
        drawLine(gc, x, y + height, x, y, color); // 左边
    }

    public static void main(String[] args) {
        launch(args);
    }
}